package com.example.aigc_education.service.impl;

import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.example.aigc_education.common.BusinessException;
import com.example.aigc_education.constant.CacheConstants;
import com.example.aigc_education.constant.WeChatConstants;
import com.example.aigc_education.domain.CozeTask;
import com.example.aigc_education.domain.Plan;
import com.example.aigc_education.domain.User;
import com.example.aigc_education.domain.vo.BindSuccessVO;
import com.example.aigc_education.domain.vo.IdVO;
import com.example.aigc_education.exception.ServiceException;
import com.example.aigc_education.exception.UserPasswordNotMatchException;
import com.example.aigc_education.integration.emuns.CozeAsyncWorkflow;
import com.example.aigc_education.integration.emuns.CozeTaskStatus;
import com.example.aigc_education.integration.processor.v2.async.GenFlashCardsAsyncProcessor;
import com.example.aigc_education.integration.processor.v2.async.GenQuizzesAsyncProcessor;
import com.example.aigc_education.integration.response.CozeAsyncResponse;
import com.example.aigc_education.integration.response.workflows.v2.CozeGenFlashCResponse;
import com.example.aigc_education.integration.response.workflows.v2.CozeGenQuizzesResponse;
import com.example.aigc_education.integration.threeparty.CozeService;
import com.example.aigc_education.mapper.CozeTaskMapper;
import com.example.aigc_education.mapper.PlanMapper;
import com.example.aigc_education.mapper.UserMapper;
import com.example.aigc_education.security.domain.LoginUser;
import com.example.aigc_education.security.service.TokenService;
import com.example.aigc_education.security.token.WeChatAuthenticationToken;
import com.example.aigc_education.service.PlanService;
import com.example.aigc_education.service.WeChatService;
import com.example.aigc_education.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class WeChatServiceImpl implements WeChatService {
    @Resource
    private AuthenticationManager authenticationManager;

    @Resource
    private TokenService tokenService;

    @Resource
    private PlanService planService;

    @Autowired
    private WeChatConstants weChatConstants;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private CozeService cozeService;

    @Autowired
    private RedisCache redisCache;

    @Resource
    private CozeTaskMapper cozeTaskMapper;

    @Resource
    private PlanMapper planMapper;


    @Resource
    private GenQuizzesAsyncProcessor genQuizzesAsyncProcessor;

    @Resource
    private GenFlashCardsAsyncProcessor genFcAsyncWFProcessor;



    @Override
    public String loginByWeChat(String code) {
        //获取openid
        String openid = getOpenid(code);
//        String openid = "oDPJf7L45EBzHlw7X9c-NIKpzZ5Q";
        log.info("openid:{}",openid);
        // 判断openid是否为空，如果为空则表示登录失败，抛出业务异常
        if(openid == null){
            log.error("openId为空");
            throw new BusinessException("获取openId异常");
        }
        //判断该该微信openId是否注册用户
        if(userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getOpenId, openid)) == null){
            throw new BusinessException("微信未注册用户");
        }
        Authentication authentication = null;
        try {
            // 构建微信认证令牌
            WeChatAuthenticationToken weChatAuthenticationToken = new WeChatAuthenticationToken(openid);
            // 将认证令牌设置到线程本地上下文中
            ThreadLocalUtil.setContext(weChatAuthenticationToken);
            // 执行用户认证
            authentication = authenticationManager.authenticate(weChatAuthenticationToken);
        } catch (Exception e) {
            if (e instanceof BadCredentialsException) {
                // openId不匹配异常处理
                throw new UserPasswordNotMatchException();
            } else {
                // 其他异常处理
                throw new ServiceException(e.getMessage());
            }
        } finally {
            // 清理线程本地上下文
            ThreadLocalUtil.clearContext();
        }

        // 获取登录用户信息并生成令牌
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        String token = tokenService.createToken(loginUser);
        log.info("登录成功 token:{}", token);
        return token;
    }

    /**
     * 微信登录绑定微信接口
     * @param wechatCode 微信登录返回的code
     * @return 绑定成功返回的VO对象，包括openid和userId
     */
    @Override
    public BindSuccessVO bindWithWeChat(String wechatCode) {
        // 获取当前登录用户的userId,判断用户是否已绑定微信
        Long userId = SecurityUtils.getUserId();
        User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getUserId, userId));
        if(user.getOpenId() != null){
            throw new BusinessException("用户已绑定微信");
        }
        //获取openid
        String openid = getOpenid(wechatCode);
        log.info("openid:{}", openid);
        if(openid == null){
            throw new BusinessException("获取openId异常");
        }

        //判断该openId是否已存在
        if(userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getOpenId, openid)) != null){
            throw new BusinessException("你的微信账号已经绑定了一个用户");
        }

        user.setOpenId(openid);
        userMapper.updateById(user);
        return new BindSuccessVO(openid, userId);
    }



    /**
     * 解绑微信接口
     * @return
     */
    @Override
    public IdVO unBindWithWeChat() {
        try {
            Long userId = SecurityUtils.getUserId();
            LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
            wrapper.eq(User::getUserId, userId)
                    .set(User::getOpenId, null);
            userMapper.update(null, wrapper);
            return new IdVO(userId);
        }catch (Exception e){
            e.printStackTrace();
            throw new BusinessException("解绑失败");
        }
    }

    /**
     * 查询学习计划生成状态
     */
    @Override
    public Boolean queryStudyPlanStatus(Long planId){
        // 判断planId是否存在
        Plan plan = Optional.ofNullable(planMapper.selectById(planId))
                .orElseThrow(() -> new BusinessException("计划不存在"));

        // 查询该计划下的所有任务
        List<CozeTask> allTasks = cozeTaskMapper.selectList(
                new LambdaQueryWrapper<CozeTask>().eq(CozeTask::getPlanId, plan.getPlanId())
        );

        // 如果没有任务记录，说明还在初始化中
        if (allTasks.isEmpty()) {
            return false;
        }

        // 查询是否有正在运行的任务
        List<CozeTask> runningTasks = allTasks.stream()
                .filter(task -> CozeTaskStatus.RUNNING.getValue().equals(task.getTaskStatus()))
                .collect(Collectors.toList());

        // 如果有正在运行的任务，则说明还在初始化中
        if (!runningTasks.isEmpty()) {
            return false;
        }
        return true;
    }



    @Override
    public boolean sendGenPlanSuccessMsg(Long planId, String toUrl) {
        String openid = SecurityUtils.getLoginUser().getUser().getOpenId();
        String templateId = weChatConstants.getTemplateId();
        //查询计划名称
        Plan plan = planService.getById(planId);
        new Thread(() -> {
            while (true){
                try {
                    Thread.sleep(1000);
                    Boolean genPlanStatus = queryStudyPlanStatus(planId);
                    if (genPlanStatus){
                        Map<String, Object> templateData = getPlanTemplateData(plan.getPlanName());
                        appletSendMessage(openid, templateId, templateData,toUrl);
                        break;
                    }
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

            }
        }).start();
        return true;
    }


    @Override
    public boolean sendGenTaskStatusMsg(String executeId, String taskType, String toUrl) {
        Long userId = SecurityUtils.getUserId();
        String taskTypeName = CozeAsyncWorkflow.fromValue(taskType).getName();
        String openid = SecurityUtils.getLoginUser().getUser().getOpenId();
        String templateId = weChatConstants.getTemplateId();
        new Thread(() -> {
            while (true){
                try {
                    Thread.sleep(1000);
                    String genTaskStatus = queryAITaskStatus(executeId,taskType);
                    if (genTaskStatus.equals("Running")){
                        continue;
                    }else if (genTaskStatus.equals("Success")){
                        if(taskType.equals(CozeAsyncWorkflow.GEN_QZ.getValue())){
                            String key = CacheConstants.USER_GEN_QUIZ_KEY + userId;
                            Map<String, CozeGenQuizzesResponse> quizCache = redisCache.getCacheObject(key);
                            if (quizCache == null) {
                                quizCache = new HashMap<>();
                            }
                            CozeGenQuizzesResponse successJob = (CozeGenQuizzesResponse)this.getSuccessJob(executeId, taskType);
                            quizCache.put(executeId,successJob);
                            redisCache.setCacheObject(key, quizCache);
                        }else {
                            String key = CacheConstants.USER_GEN_FLASH_KEY + userId;
                            Map<String, CozeGenFlashCResponse> flashCache = redisCache.getCacheObject(key);
                            if (flashCache == null) {
                                flashCache = new HashMap<>();
                            }
                            CozeGenFlashCResponse successJob = (CozeGenFlashCResponse)this.getSuccessJob(executeId, taskType);
                            flashCache.put(executeId,successJob);
                            redisCache.setCacheObject(key, flashCache);
                        }

                        Map<String, Object> templateData = getTaskTemplateData(taskTypeName,"成功");
                        appletSendMessage(openid, templateId, templateData,toUrl);
                        break;
                    }else{
                        Map<String, Object> templateData = getTaskTemplateData(taskTypeName,"失败");
                        appletSendMessage(openid, templateId, templateData,toUrl);
                        break;
                    }
                } catch (Exception e) {
                    log.error("计划状态发送异常：", e.getMessage());
                    break;
                }
            }
        }).start();
        return true;
    }

    public Object getSuccessJob(String executeId, String taskType) {
        if (CozeAsyncWorkflow.GEN_QZ.getValue().equals(taskType)) {
            CozeAsyncResponse response = genQuizzesAsyncProcessor.queryTaskStatus(executeId);
            return JsonUtils.fromJson(response.getData().get(0).getOutput(), com.example.aigc_education.integration.response.workflows.v2.CozeGenQuizzesResponse.class);
        } else if (CozeAsyncWorkflow.GEN_FC.getValue().equals(taskType)) {
            CozeAsyncResponse response = genFcAsyncWFProcessor.queryTaskStatus(executeId);
            return JsonUtils.fromJson(response.getData().get(0).getOutput(), com.example.aigc_education.integration.response.workflows.v2.CozeGenFlashCResponse.class);
        } else {
            throw new BusinessException("任务类型错误");
        }
    }

    /**
     * 查询任务的状态
     * @param executeId
     * @param taskType
     * @return
     */
    public String queryAITaskStatus(String executeId, String taskType) {
        if (CozeAsyncWorkflow.GEN_QZ.getValue().equals(taskType)) {
            CozeAsyncResponse response = genQuizzesAsyncProcessor.queryTaskStatus(executeId);
            return response.getData().get(0).getExecuteStatus();
        } else if (CozeAsyncWorkflow.GEN_FC.getValue().equals(taskType)) {
            CozeAsyncResponse response = genFcAsyncWFProcessor.queryTaskStatus(executeId);
            return response.getData().get(0).getExecuteStatus();
        } else {
            throw new BusinessException("任务类型错误");
        }
    }

    private Map<String, Object> getPlanTemplateData(String planName) {
        //计划成功模板数据
        Map<String, Object> data = new HashMap<String, Object>();
        data.put("thing1", Collections.singletonMap("value", planName + "计划生成"));
        data.put("phrase2", Collections.singletonMap("value", "成功"));
        //now转为字符串格式2023年07月01日 08:00
        // 格式化时间为微信要求的格式：2023年07月01日 08:00
        LocalDateTime now = LocalDateTime.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm");
        String formattedTime = now.format(formatter);

        data.put("time3", Collections.singletonMap("value", formattedTime));
        return data;
    }

    private Map<String, Object> getTaskTemplateData(String taskType,String status) {
        //计划成功模板数据
        Map<String, Object> data = new HashMap<String, Object>();
        data.put("thing1", Collections.singletonMap("value", taskType));
        data.put("phrase2", Collections.singletonMap("value", status));
        //now转为字符串格式2023年07月01日 08:00
        // 格式化时间为微信要求的格式：2023年07月01日 08:00
        LocalDateTime now = LocalDateTime.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm");
        String formattedTime = now.format(formatter);

        data.put("time3", Collections.singletonMap("value", formattedTime));
        return data;
    }

    public void appletSendMessage(String openid, String templateId, Map<String, Object> templateData,String toUrl) {
        String accessToken = getAccessToken();
        RestTemplate restTemplate = new RestTemplate();
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("touser", openid);
        map.put("template_id", templateId);
        map.put("page", toUrl);
        map.put("data", templateData);
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<Map<String, Object>> request = new HttpEntity<>(map, headers);
        String resultJson = restTemplate.postForObject("https://api.weixin.qq.com/cgi-bin/message/subscribe/send" +
                "?access_token=" + accessToken, request, String.class);
        Map bean = JSONUtil.parseObj(resultJson).toBean(Map.class);
        if (Integer.parseInt(bean.get("errcode").toString()) != 0) {
            log.error("发送失败， 错误码:" + bean.get("errcode") + " 错误信息:" + bean.get("errmsg"));
            throw new RuntimeException("发送失败， 错误码:" + bean.get("errcode") + " 错误信息:" + bean.get("errmsg"));
        }
    }

    @Override
    public void appletSendMessageTest(String openid, String templateId) {
        log.info("发送模板消息");
        String accessToken = getAccessToken();
        Map<String, Object> templateData = getPlanTemplateData("测试计划");
        RestTemplate restTemplate = new RestTemplate();
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("touser", openid);
        map.put("template_id", templateId);
//        map.put("page", toUrl);
        map.put("data", templateData);
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<Map<String, Object>> request = new HttpEntity<>(map, headers);
        String resultJson = restTemplate.postForObject("https://api.weixin.qq.com/cgi-bin/message/subscribe/send" +
                "?access_token=" + accessToken, request, String.class);
        Map bean = JSONUtil.parseObj(resultJson).toBean(Map.class);
        if (Integer.parseInt(bean.get("errcode").toString()) != 0) {
            log.error("发送失败， 错误码:" + bean.get("errcode") + " 错误信息:" + bean.get("errmsg"));
            throw new RuntimeException("发送失败， 错误码:" + bean.get("errcode") + " 错误信息:" + bean.get("errmsg"));
        }
    }


    private String getAccessToken() {
        // 这里可以结合redis进行缓存
        Map<String, String> map = new HashMap<>();
        map.put("grant_type", "client_credential");
        map.put("appid", weChatConstants.getAppid());
        map.put("secret", weChatConstants.getSecret());
        String json = HttpClientUtil.doGet(weChatConstants.getAccessTokenUrl(), map); // 得到微信端服务器返回的json数据
        JSONObject jsonObject = JSON.parseObject(json);
        if (jsonObject.getString("access_token") != null) {
            return jsonObject.getString("access_token");
        } else {
            throw new RuntimeException("凭证获取失败， 错误码:" + jsonObject.get("errcode") + " 错误信息:" + jsonObject.get("errmsg"));
        }
    }

    /**
     * 调用微信接口服务，获得当前微信用户的openid
     *
     * @param code
     * @return
     */
    @Override
    public String getOpenid(String code) {
        // 调用微信接口服务，获得当前微信用户的openid
        Map<String, String> map = new HashMap<>();
        map.put("appid", weChatConstants.getAppid());
        map.put("secret", weChatConstants.getSecret());
        map.put("js_code", code);
        map.put("grant_type", "authorization_code");
        String json = HttpClientUtil.doGet(weChatConstants.getLoginUrl(), map); // 得到微信端服务器返回的json数据

        JSONObject jsonObject = JSON.parseObject(json);
        String openid = jsonObject.getString("openid"); // 解析json数据，获取openid

        return openid;
    }
}
